﻿/*
 *  RacerLib - XNA Racing Game Level and Tile Management Library
 * 
 *  Copyright (C) 2009 Sebastian Schäfer
 *  
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License 
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Text;
using System.Runtime.Serialization;
using System.IO;
using System.Xml.Serialization;

using gdv.VCPR.Racer;

using Ionic.Zip;

namespace RacerEditorWIN
{
    /// <summary>
    /// A TileSet containing all allowed tiles
    /// </summary>
    [Serializable]
    public class TileSet
    {
        public static String FileName = "tileset.xml";
        public Collection<TileInfo> Tiles { get; set; }

        /// <summary>
        /// Name of the TileSet
        /// </summary>
        public String Name { get; set; }
        /// <summary>
        /// Creator of the TileSet
        /// </summary>
        public String Creator { get; set; }
        /// <summary>
        /// Base Path of the Tileset
        /// </summary>
        public String BasePath { get; set; }


        /// <summary>
        /// Create a new instance and init all variables
        /// </summary>
        public TileSet()
            :this("", "")
        {
        }

        /// <summary>
        /// Create a new instance
        /// </summary>
        /// <param name="Name">Name of the TileSet</param>
        /// <param name="Creator">Creator of the TileSet</param>
        public TileSet(String Name, String Creator)
        {
            this.Name = Name;
            this.Creator = Creator;

            init();
        }

        private void init()
        {
            Tiles = new Collection<TileInfo>();
        }

        /// <summary>
        /// Get a Bitmap representing a bitfield
        /// </summary>
        /// <param name="type">bitfield</param>
        /// <returns>Bitmap instance</returns>
        public Bitmap GetImage(int type)
        {
            foreach (TileInfo ti in Tiles)
                if (ti.Type == type)
                    return ti.Bitmap;
            return TileSet.NoBitmap();
        }

        /// <summary>
        /// Get a Bitmap representing the tile's bitfield
        /// </summary>
        /// <param name="tile"></param>
        /// <returns></returns>
        public Bitmap GetImage(Tile tile)
        {
            return GetImage(tile.Type);
        }

        /// <summary>
        /// Add a new Tile to the TileSet
        /// </summary>
        /// <param name="type">Tile type</param>
        /// <param name="Filename">Filename of the tile</param>
        /// <param name="Name">Name of the Tile</param>
        /// <param name="Bitmap">Bitmap representing the Tile</param>
        public void Add(int type, String Filename, String Name, Bitmap Bitmap)
        {
            Tiles.Add(new TileInfo(Filename, Name, type, Bitmap));
        }

        /// <summary>
        /// Save the current instance to an xml-file
        /// </summary>
        /// <param name="Filename"></param>
        /// <returns>success??</returns>
        public bool SaveXML(String Filename)
        {
            return TileSet.SaveXML(Filename, this);
        }

        /// <summary>
        /// Save the passed instance to an xml-file, always close the stream
        /// </summary>
        /// <param name="Filename"></param>
        /// <param name="TileSet">instance</param>
        /// <returns>success??</returns>
        public static bool SaveXML(String Filename, TileSet TileSet)
        {
            return SaveXML(new FileStream(Filename, FileMode.Create), TileSet, true);
        }

        /// <summary>
        /// Save the passed instance to an xml-file
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="TileSet">instance</param>
        /// <param name="closeStream"></param>
        /// <returns>success??</returns>
        public static bool SaveXML(Stream stream, TileSet TileSet, bool closeStream)
        {
            try
            {
                XmlSerializer xml = new XmlSerializer(TileSet.GetType());
                xml.Serialize(stream, TileSet);
                if (closeStream)
                    stream.Close();
                else
                    stream.Flush();
            }
            catch
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Save a complete TileSet instance to a zip-file. Stores main xml-file + image-files
        /// </summary>
        /// <param name="Filename"></param>
        /// <param name="TileSet"></param>
        /// <returns>success??</returns>
        public static bool SaveZIP(String Filename, TileSet TileSet)
        {
            ZipFile zip = new ZipFile(Filename);
                
            //clear content
            ZipEntry[] bla = new ZipEntry[zip.Entries.Count];
            zip.Entries.CopyTo(bla, 0);
            zip.RemoveEntries(bla);

            //store main 
            MemoryStream ms = new MemoryStream();
            SaveXML(ms, TileSet, false);
            zip.AddEntry(TileSet.FileName, "", ms.GetBuffer());
            zip.Save();

            //store images
            foreach (TileInfo ti in TileSet.Tiles)
            {
                MemoryStream stream = new MemoryStream();
                ti.Bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
                zip.AddEntry(ti.Filename, "images", stream.GetBuffer());
                zip.Save();
            }

            return true;
        }

        /// <summary>
        /// Load a TileSet from file
        /// </summary>
        /// <param name="Filename"></param>
        /// <returns>instance or null</returns>
        public static TileSet LoadXML(String Filename)
        {
            return TileSet.LoadXML(File.Open(Filename, FileMode.Open));
        }

        /// <summary>
        /// Load a TileSet from a stream
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="closeStream"></param>
        /// <returns>instance or null</returns>
        public static TileSet LoadXML(Stream stream, bool closeStream)
        {
            TileSet tileSet = null;
            try
            {
                XmlSerializer xml = new XmlSerializer(typeof(TileSet));
                tileSet = (TileSet)xml.Deserialize(stream);
            }
            catch (Exception e)
            {
                System.Console.Write(e.Message);
            }
            if (closeStream)
                stream.Close();
            return tileSet;
        }

        /// <summary>
        /// Load a TileSet from a stream, always close the stream
        /// </summary>
        /// <param name="stream"></param>
        /// <returns>instance or null</returns>
        public static TileSet LoadXML(Stream stream)
        {
            return LoadXML(stream, true);
        }

        /// <summary>
        /// Load a complete TileSet from a zip-file
        /// </summary>
        /// <param name="Filename"></param>
        /// <returns>instance or null</returns>
        public static TileSet LoadZIP(String Filename)
        {
            TileSet tileSet = null;

            ZipFile zf = new ZipFile(Filename);
            if (zf.EntryFileNames.Contains(TileSet.FileName))
            {
                FileStream fs = new FileStream("tmp.xml", FileMode.OpenOrCreate);
                zf.Extract(TileSet.FileName, fs);
                fs.Close();
                tileSet = TileSet.LoadXML("tmp.xml");
                System.IO.File.Delete("tmp.xml");

                foreach (TileInfo ti in tileSet.Tiles)
                {
                    if (zf.EntryFileNames.Contains("images/" + ti.Filename))
                    {
                        Stream s = new MemoryStream();
                        zf.Extract("images/" + ti.Filename, s);
                        ti.Bitmap = new Bitmap(s);
                        s.Close();
                    }
                    else
                    {
                        Console.WriteLine("image not found: " + ti.Filename);
                        ti.Bitmap = TileSet.NoBitmap();
                    }
                }
            }
            else
            {
                Console.WriteLine("Error loading " + Filename + ": no teilset.xml found --> no valid file");
            }
            return tileSet;
        }

        /// <summary>
        /// Load a TileSet, try zip-file loader, then the xml-file loader
        /// </summary>
        /// <param name="Filename"></param>
        /// <returns></returns>
        public static TileSet Load(String Filename)
        {
            if (ZipFile.IsZipFile(Filename))
                return LoadZIP(Filename);
            else
            {
                TileSet tileSet = LoadXML(Filename);
                foreach (TileInfo ti in tileSet.Tiles)
                {
                    String path = System.IO.Path.GetDirectoryName(Filename) + "\\";
                    if (!System.IO.File.Exists(path + ti.Filename))
                        path = path +"images\\";
                    if (System.IO.File.Exists(path + ti.Filename))
                        ti.Bitmap = new Bitmap(path + ti.Filename);
                    else
                        ti.Bitmap = TileSet.NoBitmap();
                }

                return tileSet;
            }
        }

        /// <summary>
        /// Static NoBitmap Method
        /// </summary>
        /// <returns>A bitmap that symbolizes that no bitmap was found/is present in the current tileset</returns>
        public static Bitmap NoBitmap()
        {
            Bitmap bmp = new Bitmap(32, 32);
            Graphics g = Graphics.FromImage(bmp);
            g.FillRectangle(Brushes.Red, new Rectangle(new Point(), bmp.Size));
            g.DrawString("NOT", new Font("Arial", 10), Brushes.Black, new PointF(1,0));
            g.DrawString("FOUND", new Font("Arial", 6), Brushes.Black, new PointF(0, 16));
            return bmp;
        }
    }
}
